// 全局错误处理中间件
import { Request, Response, NextFunction } from 'express'
import { Prisma } from '@prisma/client'

// 自定义错误类
export class AppError extends Error {
  public statusCode: number
  public isOperational: boolean

  constructor(message: string, statusCode: number = 500, isOperational: boolean = true) {
    super(message)
    this.statusCode = statusCode
    this.isOperational = isOperational

    Error.captureStackTrace(this, this.constructor)
  }
}

// 错误处理中间件
export const errorHandler = (error: Error, req: Request, res: Response, next: NextFunction) => {
  let statusCode = 500
  let message = '服务器内部错误'
  let details: any = undefined

  // 开发环境下显示详细错误信息
  const isDevelopment = process.env.NODE_ENV === 'development'

  // 处理自定义应用错误
  if (error instanceof AppError) {
    statusCode = error.statusCode
    message = error.message
  }
  // 处理Prisma数据库错误
  else if (error instanceof Prisma.PrismaClientKnownRequestError) {
    const { code, meta } = error
    
    switch (code) {
      case 'P2002': // 唯一约束违反
        statusCode = 409
        message = '数据已存在，请检查输入信息'
        if (isDevelopment && meta?.target) {
          details = { field: meta.target }
        }
        break
        
      case 'P2025': // 记录不存在
        statusCode = 404
        message = '请求的数据不存在'
        break
        
      case 'P2003': // 外键约束违反
        statusCode = 400
        message = '数据关联错误'
        break
        
      case 'P2014': // 关联记录不存在
        statusCode = 400
        message = '关联的数据不存在'
        break
        
      default:
        statusCode = 500
        message = '数据库操作失败'
        if (isDevelopment) {
          details = { code, meta }
        }
    }
  }
  // 处理Prisma验证错误
  else if (error instanceof Prisma.PrismaClientValidationError) {
    statusCode = 400
    message = '请求数据格式错误'
    if (isDevelopment) {
      details = { validation: error.message }
    }
  }
  // 处理JWT错误
  else if (error.name === 'JsonWebTokenError') {
    statusCode = 401
    message = '无效的认证令牌'
  }
  else if (error.name === 'TokenExpiredError') {
    statusCode = 401
    message = '认证令牌已过期'
  }
  // 处理验证错误（Joi等）
  else if (error.name === 'ValidationError') {
    statusCode = 400
    message = '请求参数验证失败'
    if (isDevelopment) {
      details = error.message
    }
  }
  // 处理语法错误
  else if (error instanceof SyntaxError && 'body' in error) {
    statusCode = 400
    message = '请求体格式错误'
  }
  // 处理其他错误
  else {
    // 记录未知错误
    console.error('未处理的错误:', {
      message: error.message,
      stack: error.stack,
      url: req.url,
      method: req.method,
      body: req.body,
      query: req.query,
      params: req.params
    })
    
    if (isDevelopment) {
      message = error.message
      details = { stack: error.stack }
    }
  }

  // 构建错误响应
  const errorResponse: any = {
    error: message,
    timestamp: new Date().toISOString(),
    path: req.path,
    method: req.method
  }

  // 开发环境下添加详细信息
  if (isDevelopment && details) {
    errorResponse.details = details
  }

  // 记录错误日志
  if (statusCode >= 500) {
    console.error('服务器错误:', {
      statusCode,
      message,
      url: req.url,
      method: req.method,
      userAgent: req.get('User-Agent'),
      ip: req.ip,
      stack: error.stack
    })
  } else if (statusCode >= 400 && isDevelopment) {
    console.warn('客户端错误:', {
      statusCode,
      message,
      url: req.url,
      method: req.method
    })
  }

  res.status(statusCode).json(errorResponse)
}

// 异步错误包装器
export const asyncHandler = (fn: Function) => {
  return (req: Request, res: Response, next: NextFunction) => {
    Promise.resolve(fn(req, res, next)).catch(next)
  }
}

// 404处理器
export const notFoundHandler = (req: Request, res: Response) => {
  res.status(404).json({
    error: '请求的资源不存在',
    path: req.path,
    method: req.method,
    timestamp: new Date().toISOString()
  })
}

// 常用错误创建函数
export const createError = {
  badRequest: (message: string = '请求参数错误') => new AppError(message, 400),
  unauthorized: (message: string = '未授权访问') => new AppError(message, 401),
  forbidden: (message: string = '禁止访问') => new AppError(message, 403),
  notFound: (message: string = '资源不存在') => new AppError(message, 404),
  conflict: (message: string = '资源冲突') => new AppError(message, 409),
  tooManyRequests: (message: string = '请求过于频繁') => new AppError(message, 429),
  internal: (message: string = '服务器内部错误') => new AppError(message, 500)
}